const { Duplex } = require('stream')
const fs = require('fs')

class DuplexStream extends Duplex {
  constructor({
    readableHighWaterMark,
    writableHightWaterMark,
    readFileName,
    writeFileName,
  }) {
    super({ readableHighWaterMark, writableHightWaterMark })

    this.readFileName = readFileName
    this.writeFileName = writeFileName
    this.readFd = null
    this.writeFd = null
    // 缓存块
    this.chunks = []
    // 缓存块大小
    this.chunkSize = 0
  }

  _construct(callback) {
    fs.open(this.readFileName, 'r', (err, readFd) => {
      // 如果打开文件出错，则调用回调函数并传入错误信息
      if (err) return callback(err)
      // 保存文件描述符
      this.readFd = readFd

      fs.open(this.writeFileName, 'w', (err, writeFd) => {
        // 如果打开文件出错，则调用回调函数并传入错误信息
        if (err) return callback(err)
        // 保存文件描述符
        this.writeFd = writeFd
        callback() // 调用回调函数，表示构造函数执行完毕
      })
    })
  }

  // 读取指定大小的数据
  _read(size) {
    // 创建一个指定大小的缓冲区
    const buffer = Buffer.alloc(size)
    // 从文件描述符中读取数据
    fs.read(this.readFd, buffer, 0, size, null, (err, bytesRead) => {
      // 如果读取过程中发生错误，则销毁流
      if (err) return this.destroy(err)
      // 将读取到的数据推入流中
      this.push(bytesRead > 0 ? buffer.subarray(0, bytesRead) : null)
    })
  }

  // 写入数据
  _write(chunk, encoding, callback) {
    // 将数据块加入缓存
    this.chunks.push(chunk)
    // 更新缓存块大小
    this.chunkSize += chunk.length

    // 如果缓存块大小大于等于highWaterMark，则写入文件
    if (this.chunkSize > this.writeableHighWaterMark) {
      // 将缓存块写入文件
      fs.write(this.writeFd, Buffer.concat(this.chunks), (err) => {
        // 如果写入文件出错，则返回错误
        if (err) {
          return callback(err)
        }

        // 清空缓存块和缓存块大小
        this.chunks = []
        this.chunkSize = 0
        // 调用回调函数
        callback()
      })
    } else {
      // 如果缓存块大小小于highWaterMark，则直接调用回调函数
      callback()
    }
  }

  // 结束写入
  _final(callback) {
    // 将缓存块写入文件
    fs.write(this.writeFd, Buffer.concat(this.chunks), (err) => {
      // 如果写入文件出错，则返回错误
      if (err) {
        return callback(err)
      }

      // 清空缓存块
      this.chunks = []
      // 调用回调函数
      callback()
    })
  }

  _destroy(error, callback) {
    callback(error)
  }
}

const duplex = new DuplexStream({
  readFileName: 'read.txt',
  writeFileName: 'write.txt',
})

// duplex.write(Buffer.from('this is a string 0\n'))
// duplex.write(Buffer.from('this is a string 1\n'))
// duplex.write(Buffer.from('this is a string 2\n'))
// duplex.write(Buffer.from('this is a string 3\n'))
// duplex.end(Buffer.from('end of write'))

duplex.on('data', (chunk) => {
  console.log(chunk.toString('utf-8'))
})
